/*
 * Created on Jun 14, 2004
 *
 * Paros and its related class files.
 * 
 * Paros is an HTTP/HTTPS proxy for assessing web application security.
 * Copyright (C) 2003-2004 Chinotec Technologies Company
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the Clarified Artistic License
 * as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * Clarified Artistic License for more details.
 * 
 * You should have received a copy of the Clarified Artistic License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package org.parosproxy.paros.network;

import java.io.UnsupportedEncodingException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Abstract a HTTP body in request or response messages.
 */
public class HttpBody {

	private StringBuffer body = null;
	public static final String STORAGE_CHARSET = "8859_1";
	public static final String DEFAULT_CHARSET = "8859_1";
	private String charset = DEFAULT_CHARSET;
	private boolean isChangedCharset = false;
	private String cacheString = null;
	// private static Pattern patternCharset =
	// Pattern.compile("<META +[^>]+charset=['\"]*([^>'\"])+['\"]*>",
	// Pattern.CASE_INSENSITIVE| Pattern.MULTILINE);
	private static final Pattern patternCharset = Pattern.compile(
			"<META +[^>]+charset *= *['\\x22]?([^>'\\x22;]+)['\\x22]? *>",
			Pattern.CASE_INSENSITIVE);

	public HttpBody() {
		body = new StringBuffer();
		cacheString = null;
	}

	/**
	 * Preallocate HttpBody of a certain size. A maximum of 256K is fixed to
	 * avoid overwhelming by incorrect contentlength.
	 */
	public HttpBody(int capacity) {

		if (capacity > 0) {
			if (capacity > 128000) {
				capacity = 128000;
			}
			body = new StringBuffer(capacity);
		} else {
			body = new StringBuffer();
		}
		cacheString = null;

	}

	/**
	 * Construct a HttpBody from a String.
	 * 
	 * @param data
	 */
	public HttpBody(String data) {
		this();
		if (data == null)
			return;
		setBody(data);
		cacheString = null;

	}

	public void setBody(byte[] buf) {
		// body.setLength(0);
		body = new StringBuffer(buf.length);
		append(buf);
		cacheString = null;

	}

	/**
	 * Set this HttpBody to store the data supplied.
	 * 
	 * @param data
	 * @throws UnsupportedEncodingException
	 */
	public void setBody(String data) {
		byte[] buf = null;
		try {
			// data = new String(data.getBytes(getCharset()), STORAGE_CHARSET);
			buf = data.getBytes(getCharset());

		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		setBody(buf);
		cacheString = null;

	}

	/**
	 * Append a byte array with certain length to this body.
	 * 
	 * @param buf
	 * @param len
	 */
	public void append(byte[] buf, int len) {
		if (buf == null)
			return;

		String temp = null;
		try {
			temp = new String(buf, 0, len, STORAGE_CHARSET);
		} catch (Exception e) {
			temp = new String(buf, 0, len);
		}
		body.append(temp);
		cacheString = null;

	}

	/**
	 * Append a byte array to this body.
	 * 
	 * @param buf
	 */
	public void append(byte[] buf) {
		if (buf == null)
			return;
		append(buf, buf.length);
		cacheString = null;

	}

	/**
	 * Append a String to this body.
	 * 
	 * @param buf
	 */
	public void append(String buf) {
		if (buf == null)
			return;

		body.append(buf);
		cacheString = null;

	}

	/**
	 * Get the content of the body as String.
	 */
	public String toString() {

		if (cacheString != null) {
			return cacheString;
		}

		if (isChangedCharset) {
			cacheString = toString(getCharset());
			isChangedCharset = false;
			return cacheString;
		}

		// undetermined charset;
		String html = body.toString();

		Matcher matcher = patternCharset.matcher(html);
		if (matcher.find()) {
			setCharset(matcher.group(1));
		} else {
			String utf8 = toUTF8();
			if (utf8 != null) {
				// assume to be UTF8
				setCharset("UTF8");
				isChangedCharset = false;
				cacheString = utf8;
				return cacheString;
			}

		}
		cacheString = toString(getCharset());
		return cacheString;
	}

	public String toString(String charset) {
		String result = "";
		try {
			if (charset.equalsIgnoreCase(STORAGE_CHARSET)) {
				result = body.toString();
			} else {
				result = new String(getBytes(), charset);
			}
		} catch (UnsupportedEncodingException e) {
			result = body.toString();
		}
		return result;

	}

	public byte[] getBytes() {
		byte[] result = null;
		try {
			result = body.toString().getBytes(STORAGE_CHARSET);

		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * Current length of the body.
	 * 
	 * @return
	 */
	public int length() {
		return body.length();
	}

	/**
	 * Set the current length of this body. If the current content is longer,
	 * the excessive data will be truncated.
	 * 
	 * @param length
	 */
	public void setLength(int length) {
		body.setLength(length);
		cacheString = null;

	}

	/**
	 * @return Returns the charset.
	 */
	public String getCharset() {
		return charset;
	}

	/**
	 * @param charset
	 *            The charset to set.
	 */
	public void setCharset(String charset) {
		if (charset == null || charset.equals("")) {
			return;
		}
		this.charset = charset;
		isChangedCharset = true;
	}

	private String toUTF8() {
		byte[] buf1 = getBytes();
		String utf8 = null;
		byte[] buf2 = null;
		try {
			utf8 = new String(buf1, "UTF8");
			buf2 = utf8.getBytes("UTF8");
		} catch (UnsupportedEncodingException e) {
			System.out.println("UTF8 not supported.  Using 8859_1 instead.");
			return body.toString();
		}

		if (buf1.length != buf2.length) {
			return null;
		}

		// for(int i=0; i<buf1.length; i++) {
		// if (buf1[i] != buf2[i]) {
		// return null;
		// }
		// }

		return utf8;
	}

}